/**
 * 多比例图片裁剪上传组件
 * 用户在指定弹出modal内可以自由裁剪图片并上传
 *
 * > 依赖 cropper/bootstrap 实现裁剪
 * > 依赖部分签名js 以及自定义的uploader组件上传图片
 * > 此插件由于是使用上传id在本地签名，仅内部使用
 */
(function () {
  /**
   * modal模版
   */
  var modalHtml = '<div class="modal fade" id="{{id}}" role="dialog" aria-labelledby="modalLabel" tabindex="-1">\
        <div class="md-doc modal-dialog modal-lg" role="document">\
            <div class="modal-content">\
                <div class="modal-header">\
                    <h5 class="md-title modal-title"></h5>\
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>\
                </div>\
                <div class="modal-body">\
                    <div class="md-wrap clip-wrap">\
                        <div class="clip-main">\
                            <div class="clip-canvas">\
                                <div class="img-container">\
                                    <img class="md-img" src="" alt="Picture">\
                                </div>\
                            </div>\
                            <div class="clip-btns">\
                                <div class="md-special-file pull-left mr10">\
                                    <label class="btn btn-primary" for="fileInput1">\
                                        <input type="file" class="sr-only md-file-input" id="fileInput1" name="file">\
                                        <span>选择图片</span>\
                                    </label>\
                                </div>\
                                <span class="md-notice"></span>\
                                <div class="pull-right">\
                                    <button type="button" class="md-exit-btn btn btn-default">取消</button>\
                                    <button type="button" class="md-clip-btn btn btn-primary">确定裁剪</button>\
                                </div>\
                            </div>\
                        </div>\
                        <div class="md-clip-handle clip-handle">\
                            <div class="clip-split">\
                                <div class="md-btn-group btn-group-vertical">\
                                </div>\
                                <div class="clip-handle-bottom">\
                                    <label class="btn btn-primary" for="fileInput2">\
                                        <input type="file" class="sr-only md-file-input" id="fileInput2" name="file">\
                                        <span>选择图片</span>\
                                    </label>\
                                    <label class="md-clip-sub btn btn-primary clip-sub" >\
                                        <span title="上传图片" data-original-title="submit checked picture">上传图片</span>\
                                    </label>\
                                </div>\
                            </div>\
                            <div class="md-clip-view clip-view"></div>\
                        </div>\
                    </div>\
                </div>\
            </div>\
        </div>\
        <div class="cover"><span>请稍候...</span></div>\
    </div>';

  /**
   * cache
   */
  var modalCache = {
    count: 0, // 总计多少个modal
  }

  /**
   * 默认配置
   */
  var modalConfig = {
    modalId: 'clipModal', // 默认modal的id
    console: true, //是否在控制台显示console
    area: [568, 419], //裁剪容器尺寸 这一点和在样式中设定的尺寸保持一致，不要更改
    maxBtn: 10, //最大按钮数量
    special: true, // 是否在配置只有一个时显示为单张上传模式
  }

  /**
   * 上传默认配置
   */
  var defaultUploadConfig = {
    host: { // 上传图片地址
      'oss': null,
      'qn': null
    },
    accessid: null, // ali上传图片相关权限id
    accesskey: null, // ali上传图片相关权限key
    action: 'normal',
    filename: 'test.jpg',
    onUpload: function (data) {
      console.log('上传', data)
    }
  }

  /**
   * 裁剪默认配置
   */
  var defaultClipOption = {
    // 静态文字
    title: "裁剪图片", //title文字
    // 默认图片
    blank: "", //裁剪区域加载失败显示的默认背景图
    preview: "", // 预览区域图片加载失败或默认显示的图图片
    // 上传配置
    subType: 'oss', // 上传方式 阿里为oss 七牛为qn 默认为oss
    autoJoin: false, //是否在检测到手选图片合适时跳过裁剪
    type: "jpg,jpeg,png,gif,bmp,tiff", //允许上传的图片后缀
    size: 2 //大小(MB)
  }

  /**
   * 工具方法
   */

  //转化blob对象为dataurl
  var readBlobAsDataURL = function (blob, callback) {
    var a = new FileReader();
    a.onload = function (e) {
      callback(e.target.result);
    };
    a.readAsDataURL(blob);
  }

  // 获取a和b的最大公约数
  var getMaxDivisor = function (a, b) {
    if (b === 0) {
      return a;
    }
    return getMaxDivisor(b, a % b);
  }

  /**
   * 创建裁剪实例
   * @param {object} clipOpt 裁剪配置
   * @param {object} uploadOpt 上传配置
   */
  function ComicClip(clipOpt, uploadOpt) {
    // 1.检测依赖
    if (!$) {
      throw Error('没有找到jquery');
    }
    if (!Cropper) {
      throw Error('没有找到Cropper');
    }


    // 2.参数配置
    if (clipOpt && typeof clipOpt !== 'object') {
      throw Error('裁剪配置options必须为对象');
    }
    if (uploadOpt && typeof uploadOpt !== 'object') {
      throw Error('上传配置options');
    }
    this.modalId = nowId = modalConfig.modalId + (++modalCache.count);

    // 插入html/节点缓存
    this._dom = {}; // 缓存dom节点
    this._dom.body = $('body'); // 节点：body
    this._dom.body.append(modalHtml.replace('{{id}}', this.modalId));

    this._dom.modal = $('#' + this.modalId); //容器
    this._dom.doc = this._dom.modal.find(".md-doc"); //弹窗包裹器
    this._dom.title = this._dom.modal.find(".md-title"); //容器标题
    this._dom.notice = this._dom.modal.find(".md-notice"); //容器标题
    this._dom.clipWrap = this._dom.modal.find(".md-wrap"); //裁剪板块
    this._dom.img = this._dom.modal.find(".md-img"); //用于初始化cropper的图片
    this._dom.specialFile = this._dom.modal.find(".md-special-file "); //特殊模式下的input
    this._dom.exitBtn = this._dom.modal.find(".md-exit-btn"); //取消裁剪按钮
    this._dom.clipBtn = this._dom.modal.find(".md-clip-btn"); //确认裁剪按钮
    this._dom.clipHandle = this._dom.modal.find(".md-clip-handle"); //工具和预览板块
    this._dom.btnWrapper = this._dom.modal.find(".md-btn-group"); //裁剪方式按钮组
    this._dom.fileInput = this._dom.modal.find(".md-file-input"); //裁剪input
    this._dom.sub = this._dom.modal.find(".md-clip-sub"); //提交所有按钮
    this._dom.viewWrapper = this._dom.modal.find(".md-clip-view"); //预览组容器
    this._dom.btnList = '.clip-btn-item'; //按钮
    this._dom.viewList = '.clip-view-item'; //预览容器
    this._dom.cover = this._dom.modal.find('.cover'); // 遮罩
    // 整合配置
    this.clipOptions = $.extend(true, {}, defaultClipOption, clipOpt);
    this.uploadOptions = $.extend(true, {}, defaultUploadConfig, uploadOpt);

    // host必须/尾缀
    if (this.uploadOptions.host) {
      if (this.uploadOptions.host['oss'] && this.uploadOptions.host['oss'].slice(-1) !== '/') {
        this.uploadOptions.host['oss'] += '/';
      }
      if (this.uploadOptions.host['qn'] && this.uploadOptions.host['qn'].slice(-1) !== '/') {
        this.uploadOptions.host['qn'] += '/';
      }
    } else {
      throw Error('没有传入host');
    }

    // 全局状态属性
    this._cachePic = []; // 缓存所有成功上传的图片
    this._callback = function () {}; // 所有图片上传回调，在调用use的时机可以重置一次
    this._index = null; // 当前裁剪尺寸下标
    this._opened = false; // 当前是否打开状态

    // 缓存裁剪队列
    this._queue = {};

    // 静态文字修改
    this.clipOptions.notice = "只支持" + this.clipOptions.type + ",大小不超过" + this.clipOptions.size + "M";
    this.dom('modal').hide();
    this.dom("title").text(this.clipOptions.title);
    this.dom("notice").text(this.clipOptions.notice);

    // event
    var self = this;

    // bootstrap隐藏回调:调用一次triggleClose
    this.dom("modal").on('hidden.bs.modal', function () {
      self.triggerClose();
    });

    //取消按钮关闭modal
    this.dom("exitBtn").on("click", function () {
      self.triggerClose(true);
    });

    // 切换按钮:按钮组中按钮点击切换种类
    this.dom("btnWrapper").on("click", ".clip-btn-item", function () {
      var _t = $(this);
      var index = _t.index();
      if (!_t.hasClass("active")) {
        self.active(index).reload(index);
      }
    });

    // 预览框:预览框处理情况和按钮组相同
    this.dom("viewWrapper").on("click", ".clip-view-item", function () {
      var _t = $(this);
      var index = _t.parent().index();
      if (!_t.hasClass("active")) {
        self.active(index).reload(index);
      }
    });

    // 裁剪按钮:确定裁剪按钮触发裁剪
    this.dom("clipBtn").on("click", function () {
      //在特殊模式下此按钮的功能变为裁剪后立即提交
      if (modalConfig.special) {
        self.clip(function () {
          //确定裁剪后自动填充裁剪结果到cropper,并尝试提交图片
          self.active(self._index).reload(self._index, function () {
            self._openLoading();
            setTimeout(function () {
              self.dom("sub").click()
              self._closeLoading();
            }, 200)
          });
        });
      } else {
        self.clip(function () {
          //确定裁剪后自动填充裁剪结果到cropper
          self.active(self._index).reload(self._index);
        });
      }
    });

    // 提交按钮:提交所有已裁剪的图片
    this.dom("sub").on("click", function () {
      //检查cache中是否有保存的blob
      var mask = false;
      self._queue.forEach(function (t) {
        if (t.cache.blob) {
          mask = true;
        }
      });
      if (mask) {
        if (confirm("确定要提交已经裁剪过的图片吗？")) {
          self.submit();
        }
      } else {
        alert("您还没有保存过任何裁剪后的图片！");
        return this;
      }
    });

    // 初始化cropper及相关变量
    this.clipImage = this.dom("img")[0];
    this.urlTool = window.URL || window.webkitURL; // url工具
    this._cacheUrlLink = null; // 缓存url对象生成的图片blob路径
    this._clipImageType = 'png' // 导出图片数据的格式
    this.cropperOpt = {
      container: '.img-container',
      image: this.clipImage,
      aspectRatio: 1,
      dragMode: "move",
      cropBoxMovable: false,
      cropBoxResizable: false,
      cropperDataType: false,
      minCropBoxWidth: 99999, //插件内部 做了边界判断，设为较大值是为了让裁剪框最大边抵拢边框
      actions: '#actions',
      minContainerWidth: modalConfig.area[0],
      minContainerHeight: modalConfig.area[1],
    };
    this.cropper = new Cropper(this.dom("img")[0], this.cropperOpt);

    // 初始化上传插件
    this.uploaders = {};
    this.uploaderInited = false;
    this.initUploaders();

    //为input file 绑定切换图片功能
    this.dom("fileInput").each(function () {
      self.bindFileInput(this, function (file, success, defeat) {
        //开启异步遮罩，通过载入此图片获取图片宽高判断
        self._openLoading();
        //验证图片大小，格式和最小宽高是否符合要求
        var size = file.size / 1024 / 1024;
        var type = file.type.match(/\w+$/)[0],
          typeAry = self.clipOptions.type.split(",");
        if (self.urlTool) { //支持URL接口才验证宽高
          var data = self._queue[self._index];
          var url = self.urlTool;
          var imgSrc = url.createObjectURL(file);
          var img = new Image();
          var imgH, imgW;
          img.onload = function (e) {
            imgH = this.height;
            imgW = this.width;
            url.revokeObjectURL(imgSrc);
            //进行判断
            if (size > self.clipOptions.size) {
              defeat();
              self._alert("选择图片失败，图片大小不能大于" + self.clipOptions.size + "M");
              self._closeLoading();
            } else if ($.inArray(type, typeAry) === -1) {
              defeat();
              self._alert("选择图片失败，仅限格式为：" + self.clipOptions.type + "的图片");
              self._closeLoading();
            } else if (imgW < data.param.clipsize[0] || imgH < data.param.clipsize[1]) {
              defeat();
              self._alert("选择图片失败，请选择像素宽高大于" + data.param.clipsize[0] + "x" + data.param.clipsize[1] + "的图片！")
              self._closeLoading();
            } else {
              success();
              self._closeLoading();
              //检测到图片刚好符合大小
              if (self.clipOptions.autoJoin && imgW === data.param.clipsize[0] && imgH === data.param.clipsize[1]) {
                self._confirm('您选择的图片恰好满足裁剪尺寸，是否直接保存图片？', function () {
                  readBlobAsDataURL(file, function (dataUrl) {
                    self.skipClip(dataUrl, file);
                  });
                });
              }
            }
          };
          img.onerror = function () {
            this.onload = null;
            this.src = '';
            defeat();
            self._closeLoading();
          };
          img.src = imgSrc;
        }
        return true;
      });
    });

    // error处理
    this._errorAry = [];
    this._error = function (v) {
      this._errorAry.push(v);
      alert(v);
      // this._closeLoading();
    }
  }

  // 基本原型方法
  var baseMethod = {
    /**
     * 获取内部缓存的dom节点
     * @param {string} name 节点标记
     */
    dom: function (name) {
      var d = this._dom[name];
      if (typeof d === "string") {
        d = $(d);
      }
      return d;
    },
    /**
     * 关闭控制，触发_callback，抛出已完成的图片
     * @param {boolean} handle 是否主动关闭modal
     */
    triggerClose: function (handle) {
      this._opened = false;
      this._callback(this._cachePic);
      !!handle && this._hideModal();
      return this;
    },
    /**
     * 指定index重载cropper
     * @param {number} index 载入的下标
     * @param {function} callback 回调
     */
    reload: function (index, callback) {
      var i, call, self = this;
      i = index ? index : 0; // 默认第一个
      call = typeof callback === "function" ? callback : function () {};
      var data = this._queue[i];
      if (!data) {
        return this;
      }
      if (data.cache.blob) { // 已有图片
        //如果有缓存的blob，优先读取blob到裁剪中
        this.clipReload(data.cache.blob, {
          aspectRatio: data.param["clipsize"][0] / data.param["clipsize"][1]
        }, function () {
          self._index = i;
          call();
        });
      } else { // 图片为路径
        //这里先执行数据中src的读取操作，如果图片加载失败，使用默认的预览图
        var ni = new Image();
        var nowHost = data.param.previewHost || self.uploadOptions.host[data.param.subType];
        var nowSrc = nowHost ? nowHost + data.param.pathname : self.clipOptions.blank;
        var callFn = function (src) {
          // self._comicCropper.reload(src,{
          //     aspectRatio:data.param["clipsize"][0]/data.param["clipsize"][1],
          //     ready:function(){
          //         self._closeLoading();
          //         call();
          //         self._index=i;
          //     }
          // });
          //这里有个问题，cropper对于图片自己加了随机值，所以即使做了预加载，还是会重新载入一次，会引起裁剪不到图片
          //所以这里强行将canvas里面的那张加了随机数的src，成功后才关闭遮罩，避免裁剪出空白
          self.clipReload(src, {
            aspectRatio: data.param["clipsize"][0] / data.param["clipsize"][1]
          }, function () {
            var afterSrc = self.dom("img").siblings(".cropper-container").find('.cropper-canvas > img').attr("src");
            var ni2 = new Image();
            ni2.onload = function () { //由于已经预加载过了，不用担心error
              self._closeLoading();
              self._index = i;
              call();
            };
            ni2.onerror = function () { //失败了卧槽???
              this.onload = null;
              this.src = '';
              self._error("加载图片异常，请检查默认的blank图片是否存在！");
            };
            ni2.src = afterSrc;
          });
        };
        ni.onload = function () {
          callFn(nowSrc);
        };
        ni.onerror = function () {
          this.onload = null;
          this.src = '';
          callFn(self.clipOptions.blank);
        };
        this._openLoading();
        ni.src = nowSrc;
      }
      return this;
    },
    /**
     * 执行裁剪操作(将当前cropper中的图片裁剪并缓存)
     * @param {function} callback 回调
     */
    clip: function (callBack) {
      this._openLoading();
      var self = this;
      this.getClipData("blob", function (blob) {
        if (!blob) {
          self._error("目前裁剪区域没有图片！");
          self._closeLoading();
          return false;
        }
        var data = self._queue[self._index];
        //右边预览框载入base64数据图片
        readBlobAsDataURL(blob, function (dataUrl) {
          self.dom("viewList").find("img").eq(self._index).attr("src", dataUrl);
          typeof callBack === "function" && callBack();
        });
        //缓存blob到cache中
        data.cache.blob = blob;
        self._closeLoading();
      });
      return this;
    },
    /**
     * 直接保存某张图片
     */
    skipClip: function (img, file) {
      this.dom("viewList").find("img").eq(this._index).attr("src", img);
      this._queue[this._index].cache.blob = file;
      return this;
    },
    /**
     * 提交当前缓存的所有图片 *核心方法
     */
    submit: function () { //提交保存的所有内容
      //循环提取多个数据
      var subAry = [],
        self = this;
      this._queue.forEach(function (t) {
        if (t.cache.blob) {
          subAry.push({
            text: t.param.clipname,
            host: t.param.previewHost || self.uploadOptions.host[t.param.subType],
            blob: t.cache.blob,
            name: t.param.name,
            dir: t.param.dir,
            path: t.param.pathname,
            size: t.param.clipsize,
            subType: t.param.subType,
            action: t.param.action,
            stsSign: t.param.stsSign,
            img: t.cache.img[0], //由于只有经过裁剪的才会被提取，所以这里的img必然是baseurl而不是缩放后的图片
            exp: null
          });
        }
      });
      if (subAry.length === 0) {
        this._alert("您还没有保存过任何裁剪后的图片！");
        return this;
      }
      //对每个数据进行缩放操作，使它保持指定的像素宽高
      var blobData, cv, max = subAry.length,
        count = 0;
      this._openLoading();

      subAry.forEach(function (t) {
        blobData = t.blob;
        //1.创建一个指定size的canvas
        cv = document.createElement("canvas");
        var tmpW = Math.ceil(t.size[0]);
        var tmpH = Math.ceil(t.size[1]);
        cv.width = tmpW;
        cv.height = tmpH;
        //2.将img节点绘制到canvas中
        cv.getContext('2d').drawImage(t.img, 0, 0, tmpW, tmpH);
        cv.toBlob(function (b) {
          count++;
          t.exp = b;
          if (count === max) {
            doSend();
          }
        }, t.blob.type); // 记得缓存为适当的格式
      });

      // 3.提交整理后的blob数据
      function doSend() {
        self._closeLoading();
        // 实现多个ajax依次回调执行，即使失败，下一个ajax依然会执行
        var data, formData, errorAry = [],
          successAry = [];
        self._cachePic = []; // 重置提交成功图片
        // 最后一张图片上传完毕执行回调
        var postEnd = function () {
          if (errorAry.length == 0) {
            self._alert("上传成功!");
          } else if (errorAry.length < subAry.length) {
            var erStr = '部分图片上传失败，失败的图片分别为';
            errorAry.forEach(function (t) {
              erStr += t.text + '，';
            });
            erStr += '。';
            self._alert(erStr);
          } else {
            self._alert("上传失败，请检查网络!");
          }
          //设置当前显示图片为第一张
          successAry.forEach(function (o) {
            self._cachePic.push(o);
          });
          self.uploadOptions.onUpload(subAry, successAry, errorAry);
        };
        var countAjax = function () {
          countAjax._count++;
          var i = countAjax._count;
          var max = countAjax._max;
          if (max < i) {
            self._closeLoading();
            postEnd();
            countAjax = null;
            return false;
          }
          data = subAry[i - 1];
          // 确定哪种方式上传
          if (data.subType === 'oss') {
            self.uploadOssData({
              host: data.host,
              accessid: self.uploadOptions.accessid,
              accesskey: self.uploadOptions.accesskey,
              filename: data.name, // 上传文件名称可以在这里自己指定，默认为上传图片名称
              dirname: data.dir || '', // 上传目录
              stsSign: data.stsSign // 上传时sts签名配置
            }, data.exp, function (status, res) {
              var path = data.stsSign ? res.link : data.host + data.path
              if (status) {
                successAry.push(path);
              } else {
                errorAry.push(path);
              }
              countAjax();
            })
          } else if (data.subType === 'qn') {
            self.uploadQnData({
              action: data.action || this.uploadOptions.action,
              filename: data.name, // 上传文件名称可以在这里自己指定，默认为上传图片名称
              dirname: data.dir || '' // 上传目录
            }, data.exp, function (status, res) {
              if (status) {
                successAry.push(data.host + data.path);
              } else {
                errorAry.push(data.host + data.path);
              }
              countAjax();
            });
          } else {
            self._alert('没有找到正确的上传方式');
          }
        };
        countAjax._count = 0;
        countAjax._max = subAry.length;
        self._openLoading();
        countAjax();
      }
      return this;
    },

    /**
     * 通过传入的配置初始化并弹出裁剪弹窗 *核心方法
     * 传参示例 [{
     *       "clipname": "比例1：1",
     *       "clipsize": [50, 50],
     *       "pathname": "img/test3.jpg",
     *       "previewHost": "",
     *       "suffix": ""
     *     }]
     * @param {object} config 配置列表
     * @param {function} callback 弹出回调 接收两个参数isSub(是否完成了上传图片) pic(如果完成了上传图片，会返回第一个尺寸的图片)
     */
    use: function (config, callBack) {
      if (this._opened) {
        return false;
      }
      this._opened = true;
      if (Object.prototype.toString.call(config) !== "[object Array]") {
        this._error("数据格式有误!");
        return this;
      }
      if (config.length === 0) {
        this._error("缺少数据！");
        return this;
      }
      if (config.length > modalConfig.max) {
        config.length = 7;
        this._error("最多支持7组数据！");
        return this;
      }
      if (typeof callBack === "function") { // 设置关闭弹窗时的
        this._callback = callBack;
      }
      //存入option
      var self = this,
        obj;
      self._queue = [];
      self._cachePic = []; // 重置提交成功图片
      config.forEach(function (t) {
        obj = {
          param: t,
          cache: {
            img: null, //储存预览img
            blob: null //用于储存临时保存的blob
          }
        };
        //从pathname中解析出文件名称和路径
        if (obj.param.pathname && obj.param.pathname.slice(0, 1) === '/') { // 去掉/前缀
          obj.param.pathname = obj.param.pathname.slice(1);
        }
        var parsePath = obj.param.pathname.match(/^[\s\S]*\//);
        var parseName = obj.param.pathname.match(/[^\/]*?$/);
        if (!parseName) {
          throw Error('pathname有误')
        }
        obj.param.name = parseName ? parseName[0] : 'default.jpg';
        if (parsePath.length) {
          obj.param.dir = parsePath[0];
        } else {
          obj.param.dir = ''
        }
        self._queue.push(obj)
      });
      //构建html
      this.whiteHtml();
      //按照新数据重载cropper 默认载入第一条数据的图片
      var showSpecial = modalConfig.special && config.length === 1
      this.reload(0, function () {
        //显示modal
        self._showModal(!!showSpecial);
      });
      this.active(0);
      return this;
    },
    // 根据_queue构建按钮组和预览组
    whiteHtml: function () {
      if (this._queue.length) {
        var self = this;
        var buttonHtml = '',
          viewHtml = '',
          tempData, tempAry, nowHost, i = 0;
        var rdNum = parseInt(Math.random() * 1000000);
        this._queue.forEach(function (t) {
          nowHost = t.param.previewHost || self.uploadOptions.host[t.param.subType];
          i++;
          tempData = t.param;
          //通过最大公约数获取比例
          tempAry = [];
          tempAry[0] = getMaxDivisor(tempData["clipsize"][0], tempData["clipsize"][1]);
          tempAry[1] = tempData["clipsize"][0] / tempAry[0];
          tempAry[2] = tempData["clipsize"][1] / tempAry[0];
          buttonHtml += '<label class="btn btn-primary clip-btn-item">' +
            '<span> ' + tempData["clipname"] + ' </span>\n' +
            '</label>';

          viewHtml += '<div class="clip-view-row toggle-wrap">' +
            '<div class="clip-view-box clip-view-item">' +
            '<img class="clip-view-img' + i + '" src="' + (nowHost ? nowHost + tempData["pathname"] + tempData["suffix"] + '?' + (rdNum++) : self.clipOptions.blank) + '" alt="">' +
            '</div>' +
            '<div class="clip-view-des">' + tempAry[1] + ' : ' + tempAry[2] + '</div>' +
            '</div>';
          t.cache.img = ".clip-view-img" + i;
        });
        this.dom("btnWrapper").html(buttonHtml);
        this.dom("viewWrapper").html(viewHtml);
        //转化所有的cache img 为真实的dom对象
        this._queue.forEach(function (t) {
          t.cache.img = $(t.cache.img);
        });
      } else {
        this.dom("btnWrapper").html();
        this.dom("viewWrapper").html();
      }
    },
  }

  // ui操作
  var uiMethod = {
    /**
     * 打开modal 会根据传入的状态值决定打开单文件还是多文件
     */
    _showModal: function (isSpc) {
      //针对isSpc参数显示不同的节点
      this.dom('doc')[isSpc ? 'removeClass' : 'addClass']('modal-lg')
      this.dom('clipWrap')[isSpc ? 'addClass' : 'removeClass']('clip-wrap-special')
      this.dom('specialFile')[isSpc ? 'show' : 'hide']()
      this.dom('clipHandle')[isSpc ? 'hide' : 'show']()

      this.dom("modal").modal("show"); //这里是用的bootstrap的显示方法
    },
    /**
     * 关闭modal
     */
    _hideModal: function () {
      this.dom("modal").modal("hide"); //这里是用的bootstrap的显示方法
    },
    /**
     * 关闭loading遮罩
     */
    _closeLoading: function () {
      this._dom.cover.css({
        "background-color": "transparent"
      }).hide();
      return this;
    },
    /**
     * 打开loading遮罩
     */
    _openLoading: function () {
      this._dom.cover.css({
        "background-color": "rgba(0,0,0,0.3)"
      }).show();
      return this;
    },
    //同步弹窗
    _alert: function (msg) {
      //这里使用window.alert
      alert(msg);
      return this;
    },
    //同步confirm
    _confirm: function (msg, fn) {
      //这里使用window.alert
      confirm(msg) && fn();
      return this;
    },
    //将焦点对准其中一个尺寸 会将样式调整为指定位置，并不会调整cropper裁剪源数据
    //index:序号
    active: function (index) {
      var i = index ? index : 0;
      this.dom("btnList").removeClass("active").eq(i).addClass("active");
      this.dom("viewList").removeClass("active").eq(i).addClass("active");
      return this;
    }
  }

  // 裁剪相关原型方法
  var clipMethod = {

    /*
     * 解析传入的图片并把图片类型存入_clipImageType
     * img [string/file/blob] 要重载的图片
     * 返回值：boolean 是否解析成功
     * */
    parseImgType: function (img) {
      var self = this;
      if (typeof img === "string") {
        this.clipImage.src = img;
        img.replace(/\.(.*?)$/, function (res, $1) {
          self._clipImageType = $1;
        });
        if (this._clipImageType === "jpg") {
          this._clipImageType = "jpeg";
        }
      } else {
        var URL = this.urlTool;
        if (URL) {
          if (this._cacheUrlLink) {
            URL.revokeObjectURL(this._cacheUrlLink);
          }
          this.clipImage.src = this._cacheUrlLink = URL.createObjectURL(img);
          if (img.type) {
            img.type.replace(/\/(.*?)$/, function (res, $1) {
              self._clipImageType = $1;
              return res;
            });
          } else {
            this._clipImageType = "png";
          }
        } else {
          console.error("浏览器不支持对应url对象！");
          return false;
        }
      }
      return true;
    },
    /**
     * 获取当前裁剪区域内容
     * type: [string] 'blob/dataUrl' default:blob
     * callback:回调
     */
    getClipData: function (type, callback) {
      if (!this.cropper) {
        console.error("cropper未初始化!");
        return this;
      }
      if (typeof callback !== "function") {
        console.error("缺少回调!");
        return this;
      }
      var opt = {
        fillColor: "rgba(0,0,0,0)" //强制图片外的地方透明
      };
      //如果图片类型为jpg，背景为白色
      if (this._clipImageType === 'jpeg') {
        opt.fillColor = '#fff';
      }
      var res = null;
      try {
        res = this.cropper["getCroppedCanvas"](opt);
      } catch (e) {};

      if (res) {
        // 获得图片上传数据
        if (type === "dataUrl") {
          callback(res.toDataURL('image/' + this._clipImageType));
        } else {
          // 修改获取图片类型，详见: toBlob, 第三个参数可设置质量0-1之间
          // https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toBlob
          if (res.height < 1 || res.width < 1) { // 火狐toblob无法解析宽或高为0的canvas
            callback(null);
          } else {
            res.toBlob(function (file) {
              callback(file)
            }, this._clipImageType);
          }
        }
      } else {
        callback(null)
      }
      return this;
    },
    /**
     * 调整cropper参数并重载
     * method:要让cropper执行的方法
     * option:首要参数对象(可选)
     * secOption:次要参数对象(可选)
     */
    clipChange: function (method, option, secOption) {
      if (!this.cropper) {
        console.error("cropper未初始化!");
        return this;
      }
      if (typeof method !== "string") {
        console.error("change方法参数错误！");
      }
      var opt = option ? option : {};
      var secOpt = secOption ? secOption : {}; // 暂时未使用，后面扩展功能可能会用到

      var temp1, temp2, temp3, temp4;
      var self = this;
      //参数进一步处理
      switch (method) {
        case 'aspectRatio': // 改变长宽比
          temp1 = this.cropper.getCropBoxData();
          temp2 = this.cropper.getCanvasData();
          this.cropperOpt.aspectRatio = opt;
          var cacheFn = self.cropperOpt.ready;
          this.cropperOpt.ready = function () { // 仅在本次重载后重设尺寸且不执行ready
            self.cropper.setCropBoxData(cropBoxData).setCanvasData(canvasData);
            self.cropperOpt.ready = cacheFn;
          };
          self.clipReload();
          return this;
          break;
        case 'rotate':
          // this.cropper.clear();
          break;
      }
      //执行方法
      this.cropper[method](opt, secOpt);
      //善后处理
      switch (data.method) {
        case 'rotate':
          // this.cropper.crop();
          break;
        case 'scale':
        case 'scaleX':
        case 'scaleY':
          // target.setAttribute('data-option', -data.option);
          break;
      }
      return this;
    },

    /**
     * 重设cropper为新的file、(会销毁实例并应用新的option)
     * img [string/file/blob] 要重载的图片
     * opt 重设参数
     * callback 如果设置了这个 那么opt.ready将会被替换
     */
    clipReload: function (img, opt, callback) {
      if (!this.cropper) {
        console.error("cropper未初始化!");
        return this;
      }
      // var uploadedImageURL = _this.uploadedImageURL;
      if (img) {
        if (!this.parseImgType(img)) {
          return this;
        }
      }

      if (typeof opt === "object") {
        this.cropperOpt = $.extend(true, {}, this.cropperOpt, opt)
      }
      // 重置ready
      if (typeof callback === 'function') {
        this.cropperOpt.ready = callback;
      }
      this.cropper.destroy();
      this.cropper = new Cropper(this.clipImage, this.cropperOpt);
      return this;
    },
    /**
     * 为某个file input绑定change事件，使其每次改变内容都会触发一次clipReload
     * fileDom：input原生对象 verify：验证函数，只有此函数返回false才会执行clipReload
     */
    bindFileInput: function (obj, verify) {
      if (!this.cropper) {
        console.error("cropper未初始化!");
        return this;
      }
      var URL = this.urlTool,
        self = this;
      var verifyFn = typeof verify === "function" ? verify : function () {};
      if (URL) {
        obj.onchange = function () {
          var files = this.files;
          var file;
          var _t = this;
          if (files && files.length) {
            file = files[0];
            if (/^image\/\w+/.test(file.type)) {
              //抛出两个方法给verify函数用于确定验证成功或失败
              verifyFn(file, function () {
                self.clipReload(file);
              }, function () {
                //暂无失败处理
              });
            }

            this.value = "";
          }
        };
      } else {
        obj.disabled = true; //禁止file input的点击
        // obj.parentNode.className += ' disabled';
        console.error("浏览器不支持对应url对象！");
        return this;
      }
    }
  }

  // 上传相关方法
  var uploadMethod = {
    // 初始化各个上传组件
    initUploaders: function () {
      var self = this;
      if (self.uploadOptions.subType === 'qn') {
        self.uploaders['qn'] = new PluploadUpload(null, 'qn', {
          action: self.uploadOptions.action,
          filename: self.uploadOptions.filename
        });
        self.uploaders['qn'].onInit(function () {
          self.uploaderInited = true;
        })
      }
    },
    // oss 上传
    uploadOssData: function (opt, data, callback) {
      if (opt.stsSign) {
        this.ossSTSSignUpload(opt.stsSign, data, callback)
      } else {
        this.ossDirectUpload(opt, data, callback)
      }
    },
    ossDirectUpload(opt, data, callback) {
      var call = typeof callback === 'function' ? callback : function () {};
      var policyText = {
        expiration: new Date(new Date().getTime() + (opt.uploadExpire || 36e5)), // 设置该Policy的失效时间，超过这个失效时间之后，就没有办法通过这个policy上传文件了
        conditions: [
          ['content-length-range', 0, (opt.uploadMaxSize || 1048576000)] // 设置上传文件的大小限制
        ]
      };
      var message = Base64.encode(JSON.stringify(policyText));
      var bytes = Crypto.HMAC(Crypto.SHA1, message, opt.accesskey, {
        asBytes: true
      });
      var signature = Crypto.util.bytesToBase64(bytes);

      // 设置上传图片名字
      var filename = opt.filename || 'default.jpg';
      // 上传更目录不填写，存在目录以: **/**/方式构建目录
      var dirname = (function (dirname, filename) {
        if (!dirname) return '';
        return (dirname.lastIndexOf('/') >= 0 ? dirname : dirname + '/')
      })(opt.dirname, filename);

      var formData = new FormData();
      // 将配置追加到formData数据中
      $.each({
        filename: filename,
        key: dirname + filename,
        policy: message,
        success_action_status: opt.success_action_status || '200', // 让服务端返回200,不然，默认会返回204
        OSSAccessKeyId: opt.accessid,
        signature: signature
      }, function (key, item) {
        formData.append(key, item)
      });
      // 获取到formData数据，可提POST提交套服务器
      formData.set('file', data, filename);
      $.ajax({
        url: opt.host,
        type: 'post',
        contentType: false,
        processData: false,
        async: false, //  不发送同步请求
        cache: false, // 设置为false将不会从浏览器缓存中加载请求信息
        data: formData,
        success: function (res, req) {
          call(true, res);
        },
        error: function (er) {
          call(false, er);
        }
      });
    },

    // 七牛上传
    uploadQnData: function (opt, data, callback) {
      callback = typeof callback === 'function' ? callback : function () {};
      if (!this.uploaderInited) {
        this._alert('七牛上传尚未初始化或初始化失败，请稍候再试');
        return false;
      }
      var self = this;
      var fileType = 'image/' + this._clipImageType;
      var filePath = opt.dirname + opt.filename;

      var newFile = new File([data], filePath, {
        type: fileType
      });
      this.uploaders['qn'].change({
        action: opt.action,
        filename: filePath,
        callback: function (status) {
          callback(status);
        }
      }, function (status, data) {
        if (status) {
          self.uploaders['qn'].upload(newFile, filePath);
        } else {
          callback(false, '上传失败：改变配置失败');
        }
      })
    },
    // ossSTSSignUpload
    // callback [状态, 数据]
    ossSTSSignUpload: function (opt, data, callback) {
      var _this = this;
      callback = typeof callback === 'function' ? callback : function () {};
      _this.ossSTSSignClient(opt, function (err, res) {
        if (err) {
          return callback(false, err)
        }
        var client = res.client,
          filePath = res.filePath,
          link = res.link;
        fileObj = new File([data], opt.params.filename || 'upload.jpg', {
          type: 'image/jpeg'
        });
        _this.ossMultipartUpload(client, filePath, fileObj, function (err, res2) {
          if(err) {
            callback(false, err)
          }
          if (res2) {
            res2.link = link
          }
          callback(true, res2)
        })
      })
    },
    // ossSTS签名
    ossSTSSign: function (opt, callback) {
      $.ajax({
        url: opt.url,
        type: 'post',
        data: opt.params,
        headers: {
          'content-type': 'application/x-www-form-urlencoded'
        },
        success: function (res) {
          if(res.status === 0) {
            callback(null, res.data);
          } else {
            callback('授权失败');
          }
        },
        error: function (err) {
          callback(err);
        },
      });
    },
    // 上传图片
    ossMultipartUpload: function (client, key, file, callback) {
      callback = typeof callback === 'function' ? callback : function () {};
      var errMsg = '上传失败了～要不刷新页面再试试？';
      client.multipartUpload(key, file).then(function (result) {
        if (result.res && result.res.status === 200) {
          callback(null, result);
          return;
        }
        callback(errMsg);
      }).catch(function (e) {
        callback(errMsg);
      });
    },

    // 获取路径授权
    ossSTSSignClient: function (opt, callback) {
      var _this = this;
      $.getScript('//gosspublic.alicdn.com/aliyun-oss-sdk.min.js', function (oss) {
        const Wp = OSS.Wrapper;
        _this.ossSTSSign(opt, function (err, res) {
          if (err) {
            callback('获取上传参数失败，无法进行头像上传，请检查网络');
            return;
          }
          var token = res.token
          var link = res.link
          var filePath = res.filePath
          var client = new Wp({
            region: res.region,
            accessKeyId: token.AccessKeyId,
            accessKeySecret: token.AccessKeySecret,
            stsToken: token.SecurityToken,
            bucket: res.bucket
          });
          callback(null, {
            client: client,
            filePath: filePath,
            link: link
          });
        })
      });
    },

    /**
     * dataUrl转化为blob对象
     * @param {[string]} dataurl url
     */
    dataURLtoBlob: function (dataurl) {
      var arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);

      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }

      return new Blob([u8arr], {
        type: mime
      });
    },
  }

  ComicClip.prototype = $.extend(true, {}, baseMethod, uiMethod, clipMethod, uploadMethod);
  window['ClipModal'] = ComicClip;
})();
